package br.com.gr.jcash.dao;

import br.com.gr.jcash.exception.JCashException;
import java.math.BigDecimal;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.apache.log4j.Logger;

public abstract class AbstractDAO<T, ID> implements IDAO<T, ID> {

    private static final Logger log = Logger.getLogger(AbstractDAO.class);
    private Class<T> classeEntidade;

    public AbstractDAO(Class<T> classeEntidade) {
        this.classeEntidade = classeEntidade;
    }

    protected abstract EntityManager getEntityManager();

    @Override
    public void incluir(T entidade) throws JCashException {
        try {
            getEntityManager().persist(entidade);
        } catch (Exception e) {
            log.error("Erro ao incluir: " + entidade.getClass().getCanonicalName() + e);
            throw new JCashException(e, "Erro ao incluir");
        }
    }

    @Override
    public void remover(T entidade) throws JCashException {
        try {
            entidade = atualizar(entidade);
            getEntityManager().remove(entidade);
        } catch (Exception e) {
            log.error("Erro ao excluir: " + entidade.getClass().getCanonicalName() + e);
            throw new JCashException(e, "Erro ao excluir");
        }
    }

    @Override
    public T atualizar(T entidade) throws JCashException {
        try {
            return getEntityManager().merge(entidade);
        } catch (Exception e) {
            log.error("Erro ao atualizar: " + entidade.getClass().getCanonicalName() + e);
            throw new JCashException(e, "Erro ao atualizar");
        }
    }

    protected T buscarUnico(String namedQuery, Object... params) throws JCashException {
        try {
            Query query = getEntityManager().createNamedQuery(namedQuery);
            setarParametros(query, params);
            return (T) query.getSingleResult();
        } catch (NoResultException e) {
            return null;
        } catch (Exception e) {
            log.error("Erro buscarPorNamedQuery" + e);
            throw new JCashException(e, "Erro ao buscar a entidade");
        }
    }

    public List<T> buscarTodos(String namedQuery, Object... params) throws JCashException {
        try {
            Query query = getEntityManager().createNamedQuery(namedQuery);
            setarParametros(query, params);
            return query.getResultList();
        } catch (NoResultException e) {
            return null;
        } catch (Exception e) {
            log.error("Erro ao buscar Todos" + e);
            throw new JCashException(e, "Erro ao buscar as entidades");
        }
    }

    @Override
    public T buscarPorId(ID id) throws JCashException {
        try {
            return getEntityManager().find(classeEntidade, id);
        } catch (NoResultException e) {
            return null;
        } catch (Exception e) {
            log.error("Erro ao buscarPorId" + e);
            throw new JCashException(e, "Erro ao buscar a entidade pelo ID");
        }
    }

    protected Long count(String namedQuery, Object... params) throws JCashException {
        try {
            Query query = getEntityManager().createNamedQuery(namedQuery);
            setarParametros(query, params);
            return (Long) query.getSingleResult();
        } catch (NoResultException e) {
            return null;
        } catch (Exception e) {
            log.error("Erro ao buscarCountPorNamedQuery" + e);
            throw new JCashException(e, "Erro ao contar a quantidade de registros");
        }
    }

    protected List<T> buscarPorFaixa(String namedQuery, int[] faixa, Object... params) throws JCashException {
        try {
            Query query = getEntityManager().createNamedQuery(namedQuery.trim());
            query.setFirstResult(faixa[0]);
            query.setMaxResults(faixa[1] - faixa[0]);
            setarParametros(query, params);
            return query.getResultList();
        } catch (NoResultException e) {
            return null;
        } catch (Exception ex) {
            log.error("Erro ao buscarPaginacao: " + ex.getMessage());
            throw new JCashException(ex, "Erro ao buscar entidades por paginação!");
        }
    }

    private void setarParametros(Query query, Object... params) {
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                if (params[i] != null) {
                    query.setParameter("p" + i, params[i]);
                }
            }
        }
    }
    
    protected BigDecimal buscarSoma(String namedQuery, Object... params) throws JCashException {
        try {
            Query query = getEntityManager().createNamedQuery(namedQuery);
            setarParametros(query, params);
            return (BigDecimal) query.getSingleResult();
        } catch (NoResultException e) {
            return null;
        } catch (Exception e) {
            log.error("Erro ao buscarSomaPorNamedQuery" + e);
            throw new JCashException(e, "Erro ao somar os lançamentos");
        }
        
    }
}
